package com.zengqingfa.springboot.redis.service.impl;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.zengqingfa.springboot.redis.constants.RedisCacheKeyConstants;
import com.zengqingfa.springboot.redis.entity.User;
import com.zengqingfa.springboot.redis.helper.HistorySearchHelper;
import com.zengqingfa.springboot.redis.helper.RedisIdentityHelper;
import com.zengqingfa.springboot.redis.mapper.UserMapper;
import com.zengqingfa.springboot.redis.service.UserService;
import org.redisson.Redisson;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: slfx
 * @Date: 2022-07-27
 * @Description:
 **/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedisIdentityHelper redisIdentityHelper;
    @Resource
    private HistorySearchHelper historySearchHelper;
    @Resource
    private RedissonClient redissonClient;


    @Override
    public User getById(Long id) {
        //先从缓存查询
        Object obj = redisTemplate.opsForValue().get(RedisCacheKeyConstants.USER_INFO_ID + id);
        if (Objects.nonNull(obj)) {
            User user = JSON.parseObject(String.valueOf(obj), User.class);
            return user;
        }
        //查询不到查询数据库
        User user = userMapper.selectById(id);
        return user;
    }

    @Override
    public List<User> listUser(User user) {
        List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>().like(User::getName, user.getName()));
        return users;
    }

    @Override
    public IPage<User> listUserByPage(Page<User> page, User user, String currentUserId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(user.getName())) {
            // 将用户搜索关键词存储到redis(实际场景是针对当前使用的用户)
            if (StrUtil.isNotBlank(currentUserId)) {
                String key = RedisCacheKeyConstants.HISTORY_USER_NAME_SEARCH_KEY_PREFIX + currentUserId;
                historySearchHelper.lpush(key, user.getName());
            }
            queryWrapper.like(User::getName, user.getName());
        }
        IPage<User> userIPage = userMapper.selectPage(page, queryWrapper);
        //获取到最近查询的用户信息
        List<User> records = userIPage.getRecords();
        if (CollectionUtils.isNotEmpty(records)) {
            List<Integer> userIds = records.stream().map(User::getId).collect(Collectors.toList());
            //模拟最近使用用户放入缓存
            userIds.forEach(userId -> {
                String key = RedisCacheKeyConstants.HISTORY_SEARCH_USER_PREFIX + currentUserId;
                historySearchHelper.lpush(key, userId.toString());
            });
        }
        return userIPage;
    }

    @Override
    public Integer add(User user) {
        //生成user_code
        String commonCode = redisIdentityHelper.getCommonCode(RedisCacheKeyConstants.USER_PREFIX);
        user.setUserCode(commonCode);
        userMapper.insert(user);
        User addUser = userMapper.selectById(user.getId());
        redisTemplate.opsForValue().set(RedisCacheKeyConstants.USER_INFO_ID + user.getId(), JSON.toJSONString(addUser));
        return user.getId();
    }

    @Override
    public Boolean modifyById(User user) {
        //加分布式锁
        String cacheKey = RedisCacheKeyConstants.USER_UPDATE_LOCK + user.getId();
        RLock lock = redissonClient.getLock(cacheKey);
        // 限制30秒内重复请求
        if (lock.isHeldByCurrentThread()) {
            throw new RuntimeException("请求频繁，请稍后再试");
        } else {
            //加锁（阻塞等待），默认过期时间是30秒,也可以设置时间,如果业务执行过长，Redisson会自动给锁续期
            lock.lock(30, TimeUnit.SECONDS);
        }
        int i;
        try {
            i = userMapper.updateById(user);
            //更新缓存
            User updateUser = userMapper.selectById(user.getId());
            redisTemplate.opsForValue().set(RedisCacheKeyConstants.USER_INFO_ID + user.getId(), JSON.toJSONString(updateUser));
            //其他业务1
            //其他业务2
        } finally {
            //解锁，如果业务执行完成，就不会继续续期，即使没有手动释放锁，在30秒过后，也会释放锁
            lock.unlock();
        }
        return i > 0;
    }

    @Override
    public Boolean batchUpdateByIds(List<User> users) {
        if (CollectionUtils.isEmpty(users)) {
            return false;
        }
        List<Integer> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        List<RLock> rLocks = Lists.newArrayList();
        for (Integer id : userIds) {
            rLocks.add(redissonClient.getLock(RedisCacheKeyConstants.USER_UPDATE_LOCK + id));
        }
        RLock[] rArray = rLocks.toArray(new RLock[0]);
        RedissonMultiLock redissonMultiLock = new RedissonMultiLock(rArray);
        // 限制30秒内重复请求
        if (!redissonMultiLock.tryLock()) {
            throw new RuntimeException("请求频繁，请稍后再试");
        } else {
            redissonMultiLock.lock(30, TimeUnit.SECONDS);
        }
        try {
            this.updateBatchById(users);
            //业务1
            //业务2
        } catch (Exception e) {

        } finally {
            redissonMultiLock.unlock();
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean deleteById(Integer id) {
        int i = userMapper.deleteById(id);
        //删除缓存
        redisTemplate.delete(RedisCacheKeyConstants.USER_INFO_ID + id);
        return i > 0;
    }

    @Override
    public List<String> getSearchUserNameHistory(String currentUserId) {
        String key = RedisCacheKeyConstants.HISTORY_USER_NAME_SEARCH_KEY_PREFIX + currentUserId;
        List<String> lrange = historySearchHelper.lrange(key);
        return lrange;
    }

    @Override
    public List<User> getSearchUserHistory(String currentUserId) {
        String key = RedisCacheKeyConstants.HISTORY_SEARCH_USER_PREFIX + currentUserId;
        List<String> lrange = historySearchHelper.lrange(key);
        if (CollectionUtils.isEmpty(lrange)) {
            return Lists.newArrayList();
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(User::getId, lrange);
        List<User> users = userMapper.selectList(queryWrapper);
        return users;
    }
}
